/*******************************************************************************
 *
 *    Copyright (c) Baina Info Tech Co. Ltd
 *
 *    WebzinePhoneUI
 *
 *    ScrollablePageContainer
 *    TODO File description or class description.
 *
 *    @author: dhu
 *    @since:  May 26, 2011
 *    @version: 1.0
 *
 ******************************************************************************/

package small.hero.pageview;

import java.lang.reflect.Field;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Transformation;
import android.widget.FrameLayout;
import android.widget.Scroller;


/**
 * ScrollablePageContainer of WebzinePhoneUI.
 *
 * @author dhu
 */
public class ScrollablePageContainer extends FrameLayout {

    private boolean mIsScrolling;
    private boolean mIsHorizontalScrollable = false;
    private boolean mIsVerticalScrollable = false;
    protected View mFrontView;
    protected View mBackgroundView;
    protected MotionDetector motionDetector;
    protected MotionDetector.MotionIntent motionIntent;
    private float mScrollStartX;
    private float mScrollStartY;
    private float mMoveX;
    private float mMoveY;

    private Scroller mScroller;
    private boolean mIsScrollerScrolling;
    private View mScrollToView;

    public static final int SCROLL_ORIENTATION_UNKNOW = 0;
    public static final int SCROLL_ORIENTATION_LEFT_TO_RIGHT = 1;
    public static final int SCROLL_ORIENTATION_RIGHT_TO_LEFT = 2;
    public static final int SCROLL_ORIENTATION_TOP_TO_BOTTOM = 3;
    public static final int SCROLL_ORIENTATION_BOTTOM_TO_TOP = 4;
    public static final int SCROLL_TO_INDEX_UNSPECIFIED = -1;

    public static interface PageScrollListener {
        public boolean onScrollStarted(int scrollOrientation,
                View scrollToView, int scrollToPageIndex);

        public void onScrollReseted(int scrollOrientation);

        public void onScrollFinished(int scrollOrientation,
                int scrollToPageIndex);
    }

    protected PageScrollListener mPageScrollListener;
    private int mScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
    private int mResetScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
    private boolean mCanScroll;

    private View mLeftEdge;
    private View mTopEdge;
    private View mBottomEdge;
    private View mRightEdge;
    private int mScrollToPageIndex;
    private boolean mPageTurningEnabled = false;

    public void setPageTurningEnabled(final boolean enabled) {
        mPageTurningEnabled = enabled;
        if(enabled) {
            setHorizontalScrollable(true);
        }
    }

    /**
     * Initiate a new instance of {@link ScrollablePageContainer}.
     *
     * @param context
     */
    public ScrollablePageContainer(Context context) {
        super(context);
        init(context);
    }

    /**
     * Initiate a new instance of {@link ScrollablePageContainer}.
     *
     * @param context
     * @param attrs
     */
    public ScrollablePageContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * Initiate a new instance of {@link ScrollablePageContainer}.
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public ScrollablePageContainer(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        motionDetector = new MotionDetector();
        motionIntent = MotionDetector.MotionIntent.UNKNOWN;
        mScrollToPageIndex = SCROLL_TO_INDEX_UNSPECIFIED;
        mCanScroll = false;
        mIsScrollerScrolling = false;
        mIsScrolling = false;
        mScrollToView = null;
        mScroller = new Scroller(context,
                new AccelerateDecelerateInterpolator());
        setStaticTransformationsEnabled(true);
        enableChildrenDrawingOrder();
        pageTurningInit(context);
    }

    private void pageTurningInit(Context context) {
        mLowerGradientDrawable = new GradientDrawable(
                GradientDrawable.Orientation.LEFT_RIGHT, new int[] {
                        0xff000000, 0x00000000 });
        mSideGradientDrawable = new GradientDrawable(
                GradientDrawable.Orientation.RIGHT_LEFT, new int[] {
                        0x55000000, 0x00000000 });
        mUperGradientDrawable = new GradientDrawable(
                GradientDrawable.Orientation.LEFT_RIGHT, new int[] {
                        0x55000000, 0x00000000 });
        mBackReflectGradientDrawable = new GradientDrawable(
                GradientDrawable.Orientation.RIGHT_LEFT, new int[] {
                        0x66000000, 0x00000000 });

        mLowerGradientDrawable.setShape(GradientDrawable.LINEAR_GRADIENT);
        mLowerGradientDrawable.setGradientRadius((float) (Math.sqrt(2) * 60));
        mLowerGradientDrawable.setCornerRadii(new float[] { 0, 0, 0, 0, 0, 0,
                0, 0 });

        mSideGradientDrawable.setShape(GradientDrawable.LINEAR_GRADIENT);
        mSideGradientDrawable.setGradientRadius((float) (Math.sqrt(2) * 60));
        mSideGradientDrawable.setCornerRadii(new float[] { 0, 0, 0, 0, 0, 0, 0,
                0 });

        mUperGradientDrawable.setShape(GradientDrawable.LINEAR_GRADIENT);
        mUperGradientDrawable.setGradientRadius((float) (Math.sqrt(2) * 60));
        mUperGradientDrawable.setCornerRadii(new float[] { 0, 0, 0, 0, 0, 0, 0,
                0 });

        mBackReflectGradientDrawable.setShape(GradientDrawable.LINEAR_GRADIENT);
        mBackReflectGradientDrawable.setCornerRadii(new float[] { 0, 0, 0, 0,
                0, 0, 0, 0 });
    }

    private static Field mGroupFlagsField;

    private void enableChildrenDrawingOrder() {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ECLAIR_MR1) {
            setChildrenDrawingOrderEnabled(true);
        } else {
            if (null == mGroupFlagsField) {
                try {
                    mGroupFlagsField = ViewGroup.class
                            .getDeclaredField("mGroupFlags");
                    mGroupFlagsField.setAccessible(true);
                } catch (final Exception e) {
                    Log.w(null, e);
                }
            }
            try {
                int flag = mGroupFlagsField.getInt(this);
                flag |= 0x400; // FLAG_USE_CHILD_DRAWING_ORDER
                mGroupFlagsField.setInt(this, flag);
            } catch (final Exception e) {
                Log.w(null, e);
            }
        }
    }

    /**
     * Retrieve the isHorizontalScrollable.
     *
     * @return the isHorizontalScrollable
     */
    public boolean isHorizontalScrollable() {
        return mIsHorizontalScrollable;
    }

    /**
     * Set the value of isHorizontalScrollable.
     *
     * @param scrollable
     *            the isHorizontalScrollable to set
     */
    public void setHorizontalScrollable(boolean scrollable) {
        mIsHorizontalScrollable = scrollable;
    }

    /**
     * Retrieve the isVerticalScrollable.
     *
     * @return the isVerticalScrollable
     */
    public boolean isVerticalScrollable() {
        return mIsVerticalScrollable;
    }

    /**
     * Set the value of isVerticalScrollable.
     *
     * @param scrollable
     *            the isVerticalScrollable to set
     */
    public void setVerticalScrollable(boolean scrollable) {
        mIsVerticalScrollable = scrollable;
    }

    /**
     * Set the value of pageScrollListener.
     *
     * @param pageScrollListener
     *            the pageScrollListener to set
     */
    public void setPageScrollListener(PageScrollListener pageScrollListener) {
        mPageScrollListener = pageScrollListener;
    }

    protected void startScroll(MotionEvent ev) {
        mIsScrolling = true;
        scrollInTouch(ev);
    }

    private int computeScrollOrientation(MotionEvent ev) {
        int orientation = SCROLL_ORIENTATION_UNKNOW;
        final float x = ev.getX();
        final float y = ev.getY();
        if (MotionDetector.MotionIntent.VIRTICAL_SCROLL == motionIntent) {
            orientation = y > mScrollStartY ? SCROLL_ORIENTATION_TOP_TO_BOTTOM
                    : SCROLL_ORIENTATION_BOTTOM_TO_TOP;
        } else if (MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent) {
            orientation = x > mScrollStartX ? SCROLL_ORIENTATION_LEFT_TO_RIGHT
                    : SCROLL_ORIENTATION_RIGHT_TO_LEFT;
        }
        return orientation;
    }

    protected void stopScroll(MotionEvent ev) {
        if (mIsScrolling) {
            mIsScrolling = false;
            final VelocityTracker velocityTracker = motionDetector
                    .getVelocityTracker();
            velocityTracker.computeCurrentVelocity(1000,
                    ViewConfiguration.getMaximumFlingVelocity());
            final int vx = (int) velocityTracker.getXVelocity();
            final int vy = (int) velocityTracker.getYVelocity();
            final int width = getWidth();
            final int height = getHeight();
            final int moveX = (int) mMoveX;
            final int moveY = (int) mMoveY;
            if (!mCanScroll) {
                mScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
                if (moveX > 0) {
                    mResetScrollOrientation = SCROLL_ORIENTATION_RIGHT_TO_LEFT;
                } else if (moveX < 0) {
                    mResetScrollOrientation = SCROLL_ORIENTATION_LEFT_TO_RIGHT;
                } else if (moveY > 0) {
                    mResetScrollOrientation = SCROLL_ORIENTATION_BOTTOM_TO_TOP;
                } else if (moveY < 0) {
                    mResetScrollOrientation = SCROLL_ORIENTATION_TOP_TO_BOTTOM;
                }
            } else if (MotionDetector.MotionIntent.VIRTICAL_SCROLL == motionIntent) {
                if (moveY > height / 3 || vy > 1000) {
                    mScrollOrientation = SCROLL_ORIENTATION_TOP_TO_BOTTOM;
                } else if (moveY < -height / 3 || vy < -1000) {
                    mScrollOrientation = SCROLL_ORIENTATION_BOTTOM_TO_TOP;
                } else {
                    mScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
                    if (moveY > 0) {
                        mResetScrollOrientation = SCROLL_ORIENTATION_BOTTOM_TO_TOP;
                    } else if (moveY < 0) {
                        mResetScrollOrientation = SCROLL_ORIENTATION_TOP_TO_BOTTOM;
                    }
                }
            } else if (MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent) {
                if (moveX > width / 3 || vx > 1000) {
                    mScrollOrientation = SCROLL_ORIENTATION_LEFT_TO_RIGHT;
                } else if (moveX < -width / 3 || vx < -1000) {
                    mScrollOrientation = SCROLL_ORIENTATION_RIGHT_TO_LEFT;
                } else {
                    mScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
                    if (moveX > 0) {
                        mResetScrollOrientation = SCROLL_ORIENTATION_RIGHT_TO_LEFT;
                    } else if (moveX < 0) {
                        mResetScrollOrientation = SCROLL_ORIENTATION_LEFT_TO_RIGHT;
                    }
                }
            }
            scrollToPageInner(mScrollOrientation);
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            mMoveX = mScroller.getCurrX();
            mMoveY = mScroller.getCurrY();
            postInvalidate();
        } else {
            if (mIsScrollerScrolling && mScroller.isFinished()) {
                // Scroller finished
                allScrollFinished();
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean result = false;
        if (ev.getPointerCount() > 1 || isScrolling()) {
            return super.onInterceptTouchEvent(ev);
        }
        final int action = ev.getAction();
        if (MotionEvent.ACTION_DOWN == action) {
            motionDetector.start(ev);
            motionIntent = MotionDetector.MotionIntent.UNKNOWN;
            mScrollStartX = ev.getX();
            mScrollStartY = ev.getY();
            onPreparePageTurning(ev.getX(), ev.getY());
        } else if (MotionEvent.ACTION_MOVE == action
                && motionDetector.isStarted() /*
                                             * there might be a MOVE without a
                                             * DOWN before it
                                             */) {
            if (MotionDetector.MotionIntent.UNKNOWN == motionIntent) {

                motionDetector.addMotionEvent(ev);
                motionIntent = motionDetector.detect();
                if(mPageTurningEnabled) {
                    if(MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent) {
                        result = true;
                        scrollInTouch(ev);
                    }
                }
                else if (MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent
                        && mIsHorizontalScrollable) {
                    result = true;
                    startScroll(ev);
                } else if (MotionDetector.MotionIntent.VIRTICAL_SCROLL == motionIntent
                        && mIsVerticalScrollable) {
                    result = true;
                    startScroll(ev);
                } else {
                    motionIntent = MotionDetector.MotionIntent.UNKNOWN;
                }
            } else {
                result = false;
            }
        } else if (MotionEvent.ACTION_UP == action) {
            motionDetector.reset();
            mScreenIsTouched = false;
        }
        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result = false;
        if (ev.getPointerCount() > 1) {
            return super.onTouchEvent(ev);
        }
        if(mPageTurningEnabled) {
            if (MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent) {
                final int action = ev.getAction();
                if (MotionEvent.ACTION_MOVE == action) {
                    mScreenIsTouched = true;
                    pageTurningInTouch(ev);
                    motionDetector.addMotionEvent(ev);
                } else if (MotionEvent.ACTION_UP == action
                        || MotionEvent.ACTION_CANCEL == action
                        || MotionEvent.ACTION_OUTSIDE == action) {
                    stopPageTurningInTouch(ev);
                    mScreenIsTouched = false;
                    motionDetector.reset();
                }
                result = true;
            }
        } else if ((MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent && mIsHorizontalScrollable)
                || (MotionDetector.MotionIntent.VIRTICAL_SCROLL == motionIntent && mIsVerticalScrollable)) {
            final int action = ev.getAction();

            if (MotionEvent.ACTION_MOVE == action) {
                scrollInTouch(ev);
                motionDetector.addMotionEvent(ev);
            } else if (MotionEvent.ACTION_UP == action
                    || MotionEvent.ACTION_CANCEL == action
                    || MotionEvent.ACTION_OUTSIDE == action) {
                stopScroll(ev);
                motionDetector.reset();
            }
            result = true;
        }
        return result;
    }

    private final static int MODE_NONE_STATE = -1;
    private final static int MODE_TURN_NEXT_PAGE = 0;
    private final static int MODE_TURN_PRE_PAGE = 1;
    private final static int MODE_DROP_BACK_PAGE = 3;

    private final static byte NONE_TURN_PAGE = -1;
    public final static byte FROM_UP_LEFT = 0;
    public final static byte FROM_UP_RIGHT = 1;
    public final static byte FROM_DOWN_LEFT = 2;
    public final static byte FROM_DOWN_RIGHT = 3;

    private int mTurnPageMode = MODE_NONE_STATE;
    private byte mTurnPageFrom = NONE_TURN_PAGE;

    private final Paint mTurnPagePaint = new Paint();
    private final Path path = new Path();

    private GradientDrawable mLowerGradientDrawable;
    private GradientDrawable mSideGradientDrawable;
    private GradientDrawable mUperGradientDrawable;
    private GradientDrawable mBackReflectGradientDrawable;

    private final Rect mShadowRect = new Rect();

    private final PointF mTapPoint = new PointF();
    private final PointF mBottomPoint = new PointF();
    private final PointF mSidePoint = new PointF();
    private final PointF mPeakPoint = new PointF();
    private final PointF mBottomStart = new PointF();
    private final PointF mSideStart = new PointF();
    private final PointF mBottomEnd = new PointF();
    private final PointF mSideEnd = new PointF();
    private final PointF mBottomTangent = new PointF();
    private final PointF mSideTangent = new PointF();
    private final PointF mBottomStartTangentIntersection = new PointF();
    private final PointF mSideStartTangentIntersection = new PointF();
    private final PointF mBottomEndTangentIntersection = new PointF();
    private final PointF mSideEndTangentIntersection = new PointF();
    /*
     * 在以下定义中，_之前均是缩写 b代表bottom s代表side
     */
    private float mBs_RelativeX;
    private float mBs_RelativeY;
    private float mB_RelativeX;
    private float mB_RelativeY;
    private float mBe_RelativeX;
    private float mBe_RelativeY;
    private float mSs_RelativeX;
    private float mSs_RelativeY;
    private float mS_RelativeX;
    private float mS_RelativeY;
    private float mSe_RelativeX;
    private float mSe_RelativeY;

    /**
     * 定义一个值，差不多可以理解为拖动时候的tap点的高度
     */
    private static int TAP_HEIGHT = 60;
    private static final int MAX_TAP_HEIGHT = DisplayHelper.dipToPixel(80);
    /**
     * 表示侧边阴影的宽度。
     */
    private static int SIDE_SHADOW_WIDTH = 30;
    /**
     * 表示底边阴影的宽度。
     */
    private static int LOWER_SHADOW_WIDTH = 100;

    /**
     * 定义tappoint和peakpoint连线和侧边之间的最小夹角。
     */
    private static final double MIN_ANGEL = 30 * Math.PI / 180;

//    private int mViewHeight;
//    private int mViewWidth;

    final Vector unitVectorAtYNegative = new Vector(0, -1);

    public static final int MIN_TAB_HEIGHT = 10;

    private boolean mScreenIsTouched = false;

    private final PageTurnEffectManager mPageTurnEffectManager = new PageTurnEffectManager();

    private static final int SCROLLING_MODE_NONE = -1;
    private static final int SCROLLING_MODE_AUTO = 0;
    private static final int SCROLLING_MODE_MANUAL = 1;

    private int mCurrentScrollingMode = SCROLLING_MODE_NONE;




    private void onPreparePageTurning(float x, float y) {
        final int viewWidth = getWidth();
        final int viewHeight = getHeight();
        if (x >= viewWidth / 2) {
            mTurnPageMode = MODE_TURN_NEXT_PAGE;
            mTapPoint.x = viewWidth;
            mPeakPoint.x = viewWidth;
            if (y >= viewHeight / 2) {
                mTurnPageFrom = FROM_DOWN_RIGHT;
                mTapPoint.y = viewHeight;
                mPeakPoint.y = viewHeight;
            } else {
                mTurnPageFrom = FROM_UP_RIGHT;
                mTapPoint.y = 0;
                mPeakPoint.y = 0;
            }
        } else {
            mTurnPageMode = MODE_TURN_PRE_PAGE;
            mTapPoint.x = 0;
            mPeakPoint.x = 0;
            if (y >= viewHeight / 2) {
                mTurnPageFrom = FROM_DOWN_LEFT;
                mTapPoint.y = viewHeight;
                mPeakPoint.y = viewHeight;
            } else {
                mTurnPageFrom = FROM_UP_LEFT;
                mTapPoint.y = 0;
                mPeakPoint.y = 0;
            }
        }
        // view.onStylusPress((int)mTapPoint.x, (int)mTapPoint.y);
//		view.onStylusPress(x, y);
//		myScreenIsTouched = true;
    }

    private void calculateIntersectionPoint(final PointF tapPoint) {
        keepTapPointInRange(tapPoint, mTurnPageFrom);
        calculateShadowWidth();
        final float mx = tapPoint.x;
        final float my = tapPoint.y;
        final float yValueOnRight = (mPeakPoint.y * mPeakPoint.y - my * my - (mPeakPoint.x - mx)
                * (mPeakPoint.x - mx))
                / (2 * (mPeakPoint.y - my));

        mSidePoint.x = mPeakPoint.x;
        mSidePoint.y = yValueOnRight;

        final float xValueOnBottom = (mPeakPoint.x * mPeakPoint.x - mx * mx - (mPeakPoint.y - my)
                * (mPeakPoint.y - my))
                / (2 * (mPeakPoint.x - mx));
        if (xValueOnBottom < 0) {
            mBottomPoint.x = 0;
            mBottomPoint.y = mPeakPoint.y;
        } else {
            mBottomPoint.x = xValueOnBottom;
            mBottomPoint.y = mPeakPoint.y;
        }
    }

    /**
     * 根据拉起高度确定上侧和左侧阴影宽度。
     */
    private void calculateShadowWidth() {
        SIDE_SHADOW_WIDTH = TAP_HEIGHT / 2;
        LOWER_SHADOW_WIDTH = (int) (TAP_HEIGHT * 1.5);
    }

    /**
     * 确定拉起高度。
     */
    private void calculateTapHeight(final double centerX, final double centerY,
            final double r, final double x, final double y) {
        final double currentR = Math.sqrt(Math.pow(x - centerX, 2)
                + Math.pow(y - centerY, 2));
        final double tapHeight = (-MAX_TAP_HEIGHT * Math.pow(currentR / r, 2) + MAX_TAP_HEIGHT);
        TAP_HEIGHT = (int) tapHeight;
        if (TAP_HEIGHT <= MIN_TAB_HEIGHT) {
            TAP_HEIGHT = MIN_TAB_HEIGHT;
        }

    }

    private void keepTapPointInRange(final PointF tapPoint,
            final byte turnPageFrom) {

        final double centerX;
        final double centerY;

        final double x = tapPoint.x;
        final double y = tapPoint.y;

        // 用于确认求边界y值时的符号。
        final int k;

        // ensure the radius of the circle.
        final double r;

        r = getWidth();
        switch (turnPageFrom) {
        case FROM_UP_LEFT:
            k = 1;
            centerX = getWidth();
            centerY = 0;
            break;
        case FROM_UP_RIGHT:
            k = 1;
            centerX = 0;
            centerY = 0;
            break;
        case FROM_DOWN_LEFT:
            k = -1;
            centerX = getWidth();
            centerY = getHeight();
            break;
        case FROM_DOWN_RIGHT:
            k = -1;
            centerX = 0;
            centerY = getHeight();
            break;
        default:
            k = -1;
            centerX = 0;
            centerY = 0;
        }

        // determine if the y coordinate over stepped the boundary.
        final boolean oversteped = Math.pow(x - centerX, 2)
                + Math.pow(y - centerY, 2) > Math.pow(r, 2);
        if (oversteped) {
            tapPoint.y = (float) (centerY + k
                    * Math.sqrt(Math.pow(r, 2) - Math.pow(x - centerX, 2)));
        }

        // ensure the tapHeight
        calculateTapHeight(centerX, centerY, r, x, y);

    }

    private void startAutoScrolling() {
        if (mScreenIsTouched) {
            final PointF tapPoint = mTapPoint;
            mPageTurnEffectManager.applyAnimation(tapPoint, mTurnPageFrom,
                    false, getWidth(), getHeight(), false);
            mTurnPageMode = mPageTurnEffectManager.isDropBack() ? MODE_DROP_BACK_PAGE
                    : mTurnPageMode;
            mCurrentScrollingMode = SCROLLING_MODE_MANUAL;
        } else {
            if (mTurnPageMode == MODE_TURN_NEXT_PAGE) {
                if (mTurnPageFrom == NONE_TURN_PAGE) {
                    mTurnPageFrom = FROM_DOWN_RIGHT;
                    mPeakPoint.x = getWidth();
                    mPeakPoint.y = getHeight();
                    mTapPoint.x = getWidth();
                    mTapPoint.y = getHeight();
                }
            } else if (mTurnPageMode == MODE_TURN_PRE_PAGE) {
                if (mTurnPageFrom == NONE_TURN_PAGE) {
                    mTurnPageFrom = FROM_DOWN_LEFT;
                    mPeakPoint.x = 0;
                    mPeakPoint.y = getHeight();
                    mTapPoint.x = 0;
                    mTapPoint.y = getHeight();
                }

            }
            // strideX = 1.0f * 2 * mViewWidth / AUTO_PAGE_SPEED;
            mPageTurnEffectManager.applyAnimation(mTapPoint, mTurnPageFrom,
                    false, getWidth(), getHeight(), true);
            mCurrentScrollingMode = SCROLLING_MODE_AUTO;
        }
        mScrollingInProgress = true;
        postInvalidate();
    }

    // 该方法内，k代表斜率，b如果为最后一个字符，代表截距，否则代表bottomPoint,s表示sidepoint,
    // To表示一条直线，例如bTos表示由bottompoint 到 sidepoint的直线
    private void calculatePoints() {
        final PointF sidePointF = mSidePoint;
        final PointF bottomPointF = mBottomPoint;
        final PointF tapPointF = mTapPoint;
        final PointF bottomStart = mBottomStart;
        final PointF sideStart = mSideStart;
        final PointF bottomEnd = mBottomEnd;
        final PointF sideEnd = mSideEnd;
        final PointF peakPoint = mPeakPoint;

        // 计算peakpoint tappoint连线的中垂线的斜率k, 截距b.
        final double k = MyMath.calculateK(sidePointF, bottomPointF);
        double b = 0;
        switch (mTurnPageFrom) {
        case FROM_DOWN_LEFT:
        case FROM_DOWN_RIGHT:
            b = MyMath.calculateLowerBwithDistanceAndAnotherB(TAP_HEIGHT,
                    MyMath.calculateB(sidePointF, bottomPointF), k);
            break;
        case FROM_UP_LEFT:
        case FROM_UP_RIGHT:
            b = MyMath.calculateUperBwithDistanceAndAnotherB(TAP_HEIGHT, MyMath
                    .calculateB(sidePointF, bottomPointF), k);
            break;
        }

        // 计算2段圆弧的公切线的b
        final double tangentB = (MyMath.calculateB(sidePointF, bottomPointF) + b) / 2;

        // 计算过sidepoint和bottompoint的2条垂直于公切线的直线的k,b
        final double b_b = bottomPointF.y + bottomPointF.x / k;
        final double s_b = sidePointF.y + sidePointF.x / k;

        final double bTot_k = MyMath.calculateK(bottomPointF, tapPointF);
        final double bTot_b = MyMath.calculateB(bottomPointF, tapPointF);

        final double sTot_k = MyMath.calculateK(sidePointF, tapPointF);
        final double sTot_b = MyMath.calculateB(sidePointF, tapPointF);

        // 计算各个交点的坐标，再根据这些交点的坐标来画线。
        // 底边的弧线起点
        bottomStart.y = peakPoint.y;
        bottomStart.x = (float) ((bottomStart.y - b) / k);
        // 侧边弧线起点
        sideStart.x = peakPoint.x;
        sideStart.y = (float) (k * sideStart.x + b);
        // 底边弧线终点，需要计算两条直线的交点。
        MyMath.calculateIntersection(k, b, bTot_k, bTot_b, bottomEnd);
        // 侧边弧线终点
        MyMath.calculateIntersection(k, b, sTot_k, sTot_b, sideEnd);
        // 公切线与bottom弧的切点
        MyMath.calculateIntersection(k, tangentB, -1 / k, b_b, mBottomTangent);
        // 公切线与side弧的切点
        MyMath.calculateIntersection(k, tangentB, -1 / k, s_b, mSideTangent);
        // 为了实现path的闭合，需要公切线与屏幕边框之间的交点坐标，来绘制往回的bezier.
        mBottomStartTangentIntersection.y = peakPoint.y;
        mBottomStartTangentIntersection.x = (float) ((mBottomStartTangentIntersection.y - tangentB) / k);
        mSideStartTangentIntersection.x = peakPoint.x;
        mSideStartTangentIntersection.y = (float) (k
                * mSideStartTangentIntersection.x + tangentB);

        // 计算bottom start point,bottom point 在 canvas变换后 的相对坐标
        {
            mB_RelativeX = SIDE_SHADOW_WIDTH;
            mBs_RelativeX = SIDE_SHADOW_WIDTH
                    - MyMath.calculatePointsDistant(bottomStart, bottomEnd)
                    * (MyMath.calculatePointsDistant(tapPointF, sidePointF) / MyMath
                            .calculatePointsDistant(bottomPointF, sidePointF));
            mBe_RelativeX = SIDE_SHADOW_WIDTH;
            // 继续需要计算2条直线的交点，两条直线为别是：上侧阴影边界直线与过bottom start与侧阴影平行的直线
            double uperB = 0;
            double lowerB = 0;
            switch (mTurnPageFrom) {
            case FROM_DOWN_LEFT:
            case FROM_DOWN_RIGHT:
                uperB = MyMath.calculateLowerBwithDistanceAndAnotherB(
                        SIDE_SHADOW_WIDTH, sTot_b, sTot_k);
                lowerB = MyMath.calculateUperBwithDistanceAndAnotherB(
                        SIDE_SHADOW_WIDTH - mBs_RelativeX, bTot_b, bTot_k);
                break;
            case FROM_UP_LEFT:
            case FROM_UP_RIGHT:
                uperB = MyMath.calculateUperBwithDistanceAndAnotherB(
                        SIDE_SHADOW_WIDTH, sTot_b, sTot_k);
                lowerB = MyMath.calculateLowerBwithDistanceAndAnotherB(
                        SIDE_SHADOW_WIDTH - mBs_RelativeX, bTot_b, bTot_k);
                break;
            }
            final PointF temp = new PointF();
            MyMath.calculateIntersection(bTot_k, lowerB, sTot_k, uperB, temp);
            mB_RelativeY = SIDE_SHADOW_WIDTH
                    + MyMath.calculatePointsDistant(tapPointF, bottomPointF);
            mBs_RelativeY = MyMath.calculatePointsDistant(temp, bottomStart);
            mBe_RelativeY = SIDE_SHADOW_WIDTH
                    + MyMath.calculatePointsDistant(tapPointF, bottomEnd);
            if (mTurnPageFrom == FROM_UP_RIGHT
                    || mTurnPageFrom == FROM_DOWN_LEFT) {
                mB_RelativeY = -mB_RelativeY;
                mBs_RelativeY = -mBs_RelativeY;
                mBe_RelativeY = -mBe_RelativeY;
            }
        }
        // 计算side start , side point, 在canvas变换后的相对坐标
        {
            mS_RelativeX = 0;
            mS_RelativeY = SIDE_SHADOW_WIDTH
                    + MyMath.calculatePointsDistant(tapPointF, sidePointF);
            mSe_RelativeX = 0;
            mSe_RelativeY = SIDE_SHADOW_WIDTH
                    + MyMath.calculatePointsDistant(tapPointF, sideEnd);
            final float temp = MyMath
                    .calculatePointsDistant(sideStart, sideEnd);
            mSs_RelativeX = temp
                    * (MyMath.calculatePointsDistant(tapPointF, bottomPointF) / MyMath
                            .calculatePointsDistant(bottomPointF, sidePointF));
            mSs_RelativeY = mSe_RelativeY
                    + temp
                    * (MyMath.calculatePointsDistant(sidePointF, tapPointF) / MyMath
                            .calculatePointsDistant(bottomPointF, sidePointF));
            if (mTurnPageFrom == FROM_UP_RIGHT
                    || mTurnPageFrom == FROM_DOWN_LEFT) {
                mSs_RelativeX = -mSs_RelativeX;
            }
        }
        // 计算公切线与bottom,tap,side points之间连线的交点
        MyMath.calculateIntersection(bTot_k, bTot_b, k, tangentB,
                mBottomEndTangentIntersection);
        MyMath.calculateIntersection(sTot_k, sTot_b, k, tangentB,
                mSideEndTangentIntersection);

    }

    private int mX;
    private int mY;

    private boolean mScrollingInProgress = false;

    protected void pageTurningInTouch(MotionEvent ev) {
        mX = (int) ev.getX();
        mY = (int) ev.getY();
        if(mX >= getWidth()) {
            mX = getWidth() - 1;
        } else if (mX <= 0) {
            mX = 1;
        }
        if(mY >= getHeight()) {
            mY = getHeight() - 1;
        } else if (mY <= 0) {
            mY = 1;
        }
        if(mTurnPageMode == MODE_DROP_BACK_PAGE) {
            return;
        }
        mTapPoint.x = mX;
        mTapPoint.y = mY;
        calculateIntersectionPoint(mTapPoint);
        postInvalidate();
    }

    private static final int X_RANGE = 10;
    private static final int Y_RANGE = 10;

    protected void stopPageTurningInTouch(MotionEvent ev) {
        final float x = ev.getX();
        final float y = ev.getY();
        final float dx = Math.abs(x - mScrollStartX);
        final float dy = Math.abs(y - mScrollStartY);
        // means it is a click event.
        mTapPoint.x = x;
        mTapPoint.y = y;
        startAutoScrolling();
    }

    protected void scrollInTouch(MotionEvent ev) {
        final float x = ev.getX();
        final float y = ev.getY();
        final int orientation = computeScrollOrientation(ev);
        if (mScrollOrientation != orientation && mPageScrollListener != null) {
            mBackgroundView.setVisibility(VISIBLE);
            mCanScroll = mPageScrollListener.onScrollStarted(orientation,
                    mBackgroundView, mScrollToPageIndex);
            mScrollOrientation = orientation;
            mIsPagePrepared = false;
        }
        if (MotionDetector.MotionIntent.VIRTICAL_SCROLL == motionIntent) {
            mMoveX = 0;
            if (mCanScroll) {
                mMoveY = y - mScrollStartY;
            } else {
                mMoveY = (y - mScrollStartY) / 2;
            }
        } else if (MotionDetector.MotionIntent.HORIZONTAL_SCROLL == motionIntent) {
            if (mCanScroll) {
                mMoveX = x - mScrollStartX;
            } else {
                mMoveX = (x - mScrollStartX) / 2;
            }
            mMoveY = 0;
        }
        invalidate();
    }

    public boolean isScrolling() {
        return mIsScrolling || mIsScrollerScrolling;
    }

    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        if (childCount - 1 == i) {
            return indexOfChild(mFrontView);
        }
        if (getChildAt(i) == mFrontView) {
            return childCount - 1;
        }
        return i;
    }

    @Override
    protected boolean getChildStaticTransformation(View child, Transformation t) {
        if (isScrolling()) {
            final int moveX = (int) mMoveX;
            final int moveY = (int) mMoveY;
            final int width = getWidth();
            final int height = getHeight();
            t.setTransformationType(Transformation.TYPE_MATRIX);
            final Matrix matrix = t.getMatrix();
            t.clear();

            int toX = 0;
            int toY = 0;
            if (child == mFrontView) {
                toX = moveX;
                toY = moveY;
            } else if (child == mBackgroundView) {
                if (mCanScroll) {
                    if (moveX != 0) {
                        toX = moveX > 0 ? (moveX - width) : (moveX + width);
                    }
                    if (moveY != 0) {
                        toY = moveY > 0 ? (moveY - height) : (moveY + height);
                    }
                }
            } else if (!mCanScroll) {
                if (child == mLeftEdge) {
                    toX = moveX - child.getWidth();
                    toY = 0;
                } else if (child == mRightEdge) {
                    toX = width + moveX;
                    toY = 0;
                } else if (child == mTopEdge) {
                    toX = 0;
                    toY = moveY - child.getHeight();
                } else if (child == mBottomEdge) {
                    toX = 0;
                    toY = height + moveY;
                }
            }
            applyMatrix(matrix, child, toX, toY);
            return true;
        }
        super.getChildStaticTransformation(child, t);
        return false;
    }

    private void applyMatrix(Matrix m, View child, int toX, int toY) {
        final int left = child.getLeft();
        final int top = child.getTop();
        final int dx = toX - left;
        final int dy = toY - top;
        m.postTranslate(dx, dy);
    }


    private void resetPageTurning() {
        mTurnPageMode = MODE_NONE_STATE;
        mTurnPageFrom = NONE_TURN_PAGE;
        mScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
    }

    private Bitmap mFrontPage;
    private Bitmap mBackPage;
    private Canvas mFrontCanvas;
    private Canvas mBackCanvas;
    private boolean mIsPagePrepared = false;

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (mPageTurningEnabled) {
            boolean stopPageTurning = false;
            if (mScrollingInProgress) {
                stopPageTurning = !mPageTurnEffectManager.animate(mTapPoint);
                calculateIntersectionPoint(mTapPoint);
            }
            if (stopPageTurning) {
                mScrollingInProgress = false;
                if(!mPageTurnEffectManager.isDropBack()) {
                    swapFontAndBackground();
                    if(mPageScrollListener != null) {
                        mPageScrollListener.onScrollReseted(mScrollOrientation);
                    }
                } else {
                    if(mPageScrollListener != null) {
                        mPageScrollListener.onScrollFinished(mScrollOrientation, mScrollToPageIndex);
                    }
                }
                resetPageTurning();
                super.dispatchDraw(canvas);
            } else if(mScrollingInProgress || mScreenIsTouched){
                if(mFrontPage == null || mFrontPage.isRecycled()) {
                    mFrontPage = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
                    mFrontCanvas = new Canvas(mFrontPage);
                }
                if(mBackPage == null || mBackPage.isRecycled()) {
                    mBackPage = Bitmap.createBitmap(getWidth(),getHeight(), Config.ARGB_8888);
                    mBackCanvas = new Canvas(mBackPage);
                }
                if(!mIsPagePrepared) {
                    mFrontCanvas.drawColor(Color.TRANSPARENT);
                    mBackCanvas.drawColor(Color.TRANSPARENT);
                    super.drawChild(mFrontCanvas, mFrontView, 0);
                    super.drawChild(mBackCanvas, mBackgroundView, 0);
                    mIsPagePrepared = true;
                }
                calculatePoints();
                //画第二页可见部分
                final Path downer = drawNextPageArea(canvas, mSideStart, mSideStartTangentIntersection,
                        mSideTangent, mBottomTangent,
                        mBottomStartTangentIntersection, mBottomStart, mPeakPoint,
                        mBackPage, 0);
                //TODO 画下侧阴影
                drawShadows(canvas);
                  //画第一页可见部分
                drawCurrentPageArea(canvas, mFrontPage, downer);
                // 画两侧阴影
                drawSideShadows(canvas, mTapPoint, mBottomPoint, mTurnPageFrom);
                //画第一页背面
                drawCurrentPageBack(canvas, mFrontPage);
                // 画反光阴影
                drawPageBackShadow(canvas, mSideTangent, mBottomTangent,
                        mSideEndTangentIntersection, mBottomEndTangentIntersection,
                        mSideStart, mSideEnd, mBottomEnd);
                if(mScrollingInProgress) {
                    postInvalidate();
                }
            } else {
                super.dispatchDraw(canvas);
            }
        } else {
            super.dispatchDraw(canvas);
        }
    }

    private int calculateShadowLength(final PointF translatePoint,
            final PointF bottomPoint) {
        int length = 0;
        length = (int) Math.sqrt(Math.pow(translatePoint.x - bottomPoint.x, 2)
                + Math.pow(translatePoint.y - bottomPoint.y, 2));
        return length;

    }

    private void drawPageBackShadow(Canvas canvas, final PointF sideTangent,
            final PointF bottomTangent, final PointF sideEndTangentIntersect,
            final PointF bottomEndTangentIntersect, final PointF sideStart,
            final PointF sideEnd, final PointF bottomEnd) {
        canvas.save();
        if (mTurnPageFrom == FROM_DOWN_RIGHT || mTurnPageFrom == FROM_UP_LEFT) {
            canvas.translate(sideTangent.x, sideTangent.y);
        } else {
            canvas.translate(bottomTangent.x, bottomTangent.y);
        }
        final Vector vector = new Vector();
        vector.x = sideTangent.x - bottomTangent.x;
        vector.y = sideTangent.y - bottomTangent.y;
        final float degrees = MyMath.calculateShadowRotateAngle(
                unitVectorAtYNegative, vector, mTurnPageFrom);
        canvas.rotate(degrees);

        // 需要计算一些相对坐标
        final float sideIntersection_y;
        final float bottomIntersection_y;
        final float sideEnd_y;
        final float bottomEnd_y;
        final int leftBound;
        if (mTurnPageFrom == FROM_DOWN_RIGHT || mTurnPageFrom == FROM_UP_LEFT) {
            sideIntersection_y = MyMath.calculatePointsDistant(sideTangent,
                    sideEndTangentIntersect);
            bottomIntersection_y = MyMath.calculatePointsDistant(sideTangent,
                    bottomEndTangentIntersect);
            sideEnd_y = MyMath.calculatePointsDistant(sideStart, sideEnd) / 2;
            bottomEnd_y = MyMath.calculatePointsDistant(sideEnd, bottomEnd)
                    + sideEnd_y;
            leftBound = -TAP_HEIGHT / 2;
        } else {
            sideIntersection_y = MyMath.calculatePointsDistant(bottomTangent,
                    sideEndTangentIntersect);
            bottomIntersection_y = MyMath.calculatePointsDistant(bottomTangent,
                    bottomEndTangentIntersect);
            bottomEnd_y = MyMath
                    .calculatePointsDistant(mBottomStart, bottomEnd) / 2;
            sideEnd_y = MyMath.calculatePointsDistant(bottomEnd, sideEnd)
                    + bottomEnd_y;
            leftBound = -TAP_HEIGHT / 2;
        }

        final Path path = this.path;
        path.reset();
        path.moveTo(0, 0);
        path.lineTo(0, MyMath
                .calculatePointsDistant(sideTangent, bottomTangent));
        if (mTurnPageFrom == FROM_DOWN_RIGHT || mTurnPageFrom == FROM_UP_LEFT) {
            path.quadTo(0, bottomIntersection_y, leftBound, bottomEnd_y);
            path.lineTo(leftBound, sideEnd_y);
            path.quadTo(0, sideIntersection_y, 0, 0);
        } else {
            path.quadTo(0, sideIntersection_y, leftBound, sideEnd_y);
            path.lineTo(leftBound, bottomEnd_y);
            path.quadTo(0, bottomIntersection_y, 0, 0);
        }
        path.close();
        canvas.clipPath(path);
        mShadowRect.set(leftBound, 0, 0, calculateShadowLength(sideTangent,
                bottomTangent));
        mBackReflectGradientDrawable.setBounds(mShadowRect);
        mBackReflectGradientDrawable.draw(canvas);
        canvas.restore();
    }

    private void drawSideShadows(final Canvas canvas, final PointF tapPoint,
            final PointF bottomPoint, byte turnPageFrom) {
        canvas.save();
        final int uperLength = (int) MyMath.calculatePointsDistant(bottomPoint,
                tapPoint)
                + SIDE_SHADOW_WIDTH;
        final int lowerLength = (int) (MyMath.calculatePointsDistant(tapPoint,
                mSidePoint) + SIDE_SHADOW_WIDTH);
        final int height = Math.max(uperLength, lowerLength);
        if (turnPageFrom == FROM_DOWN_RIGHT || turnPageFrom == FROM_UP_LEFT) {
            mShadowRect.set(0, 0, SIDE_SHADOW_WIDTH, height);
        } else {
            mShadowRect.set(0, -height, SIDE_SHADOW_WIDTH, 0);
        }
        mSideGradientDrawable.setBounds(mShadowRect);
        canvas.translate(tapPoint.x, tapPoint.y);
        final Vector lowerLineVector = new Vector();
        lowerLineVector.x = tapPoint.x - bottomPoint.x;
        lowerLineVector.y = tapPoint.y - bottomPoint.y;
        final float lowerLineDegree = MyMath.calculateShadowRotateAngle(
                unitVectorAtYNegative, lowerLineVector, mTurnPageFrom);
        canvas.rotate(lowerLineDegree);
        if (turnPageFrom == FROM_DOWN_RIGHT || turnPageFrom == FROM_UP_LEFT) {
            canvas.translate(-SIDE_SHADOW_WIDTH, -SIDE_SHADOW_WIDTH);
        } else {
            canvas.translate(-SIDE_SHADOW_WIDTH, SIDE_SHADOW_WIDTH);
        }
        final Path path = new Path(this.path);
        path.reset();

        path.moveTo(mBs_RelativeX, mBs_RelativeY);
        path.quadTo(mB_RelativeX, mB_RelativeY, mBe_RelativeX, mBe_RelativeY);
        if (turnPageFrom == FROM_DOWN_RIGHT || turnPageFrom == FROM_UP_LEFT) {
            path.lineTo(SIDE_SHADOW_WIDTH, SIDE_SHADOW_WIDTH);
        } else {
            path.lineTo(SIDE_SHADOW_WIDTH, -SIDE_SHADOW_WIDTH);
        }
        path.lineTo(0, 0);
        if (mBs_RelativeX > 0) {
            path.lineTo(0, mBs_RelativeY);
        } else {
            path.lineTo(mBs_RelativeX, 0);
        }
        path.lineTo(mBs_RelativeX, mBs_RelativeY);
        path.close();
        canvas.clipPath(path);
        mSideGradientDrawable.draw(canvas);
        canvas.restore();

        // 画上侧阴影
        canvas.save();
        canvas.translate(tapPoint.x, tapPoint.y);
        canvas.rotate(lowerLineDegree - 90);
        canvas.translate(0, -SIDE_SHADOW_WIDTH);

        if (turnPageFrom == FROM_DOWN_RIGHT || turnPageFrom == FROM_UP_LEFT) {
            mUperGradientDrawable.setBounds(mShadowRect);
        } else {
            mShadowRect.set(-SIDE_SHADOW_WIDTH, 0, 0, height);
            mSideGradientDrawable.setBounds(mShadowRect);
        }
        path.reset();
        path.moveTo(mSs_RelativeX, mSs_RelativeY);
        path.quadTo(mS_RelativeX, mS_RelativeY, mSe_RelativeX, mSe_RelativeY);
        path.lineTo(0, SIDE_SHADOW_WIDTH);
        if (turnPageFrom == FROM_DOWN_RIGHT || turnPageFrom == FROM_UP_LEFT) {
            path.lineTo(SIDE_SHADOW_WIDTH, 0);
        } else {
            path.lineTo(-SIDE_SHADOW_WIDTH, 0);
        }
        if (Math.abs(mSs_RelativeX) > SIDE_SHADOW_WIDTH) {
            path.lineTo(mSs_RelativeX, 0);
        } else {
            path.lineTo(SIDE_SHADOW_WIDTH, mSs_RelativeY);
        }
        path.lineTo(mSs_RelativeX, mSs_RelativeY);
        path.close();
        canvas.clipPath(path);
        if (turnPageFrom == FROM_DOWN_RIGHT || turnPageFrom == FROM_UP_LEFT) {
            mUperGradientDrawable.draw(canvas);
        } else {
            mSideGradientDrawable.draw(canvas);
        }
        canvas.restore();
    }

    private void drawShadows(final Canvas canvas) {
        // draw the shadow

        float length;

        // Draw the fold line shadow.
        canvas.save();
        length = calculateShadowLength(mSideStart, mBottomStart);

        switch (mTurnPageFrom) {
        case FROM_DOWN_RIGHT:
        case FROM_UP_LEFT:
            mShadowRect.set(0, 0, LOWER_SHADOW_WIDTH, (int) length);
            break;
        case FROM_UP_RIGHT:
        case FROM_DOWN_LEFT:
            mShadowRect.set(0, -(int) length, LOWER_SHADOW_WIDTH, 0);
            break;

        }

        // mShadowRect.set(0, 0, LOWER_SHADOW_WIDTH, (int) length);
        mLowerGradientDrawable.setBounds(mShadowRect);
        canvas.translate(mSideStart.x, mSideStart.y);
        final Vector folderLineVector = new Vector();
        folderLineVector.x = mSideStart.x - mBottomStart.x;
        folderLineVector.y = mSideStart.y - mBottomStart.y;
        final float foldLineDegree = MyMath.calculateShadowRotateAngle(
                unitVectorAtYNegative, folderLineVector, mTurnPageFrom);
        canvas.rotate(foldLineDegree);
        mLowerGradientDrawable
                .setGradientType(GradientDrawable.LINEAR_GRADIENT);
        mLowerGradientDrawable.setCornerRadii(new float[] { 0, 0, 0, 0, 0, 0,
                0, 0 });
        mLowerGradientDrawable.draw(canvas);
        canvas.restore();

    }

    private Path drawCurrentPageBack(final Canvas canvas, final Bitmap child) {
        final Path upper = new Path();
        final Paint linePaint = new Paint();
        linePaint.setColor(Color.WHITE);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(0);
        linePaint.setStyle(Paint.Style.FILL);

        // draw the upon triangle
        upper.reset();
        upper.moveTo(mSideStart.x, mSideStart.y);
        upper.quadTo(mSidePoint.x, mSidePoint.y, mSideEnd.x, mSideEnd.y);
        upper.lineTo(mTapPoint.x, mTapPoint.y);
        upper.lineTo(mBottomEnd.x, mBottomEnd.y);
        upper
                .quadTo(mBottomPoint.x, mBottomPoint.y, mBottomStart.x,
                        mBottomStart.y);
        upper
                .quadTo(mBottomStartTangentIntersection.x,
                        mBottomStartTangentIntersection.y, mBottomTangent.x,
                        mBottomTangent.y);
        upper.lineTo(mSideTangent.x, mSideTangent.y);
        upper.quadTo(mSideStartTangentIntersection.x, mSideStartTangentIntersection.y,
                mSideStart.x, mSideStart.y);

        final RectF rectF = new RectF();
        upper.computeBounds(rectF, false);
        // Draw the paper back.

        canvas.drawPath(upper, linePaint);
        upper.close();

        //TODO make paint a field variable.
        final Paint paint = new Paint();
        paint.setAlpha(150);

        // calculate the unit vector of the fold line.
        float ux = mBottomPoint.x - mSidePoint.x;
        float uy = mBottomPoint.y - mSidePoint.y;
        final float length = (float) Math.sqrt(ux * ux + uy * uy);
        ux /= length;
        uy /= length;

        final Matrix matrix = new Matrix();
        // We make a reflect transformation alone the line y = u * x, while u =
        // uy / ux.
        // See this link for detail
        // (http://zh.wikipedia.org/zh/%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5#.E5.8F.8D.E5.B0.84)
        matrix.setValues(new float[] { 2 * ux * ux - 1, 2 * ux * uy, 0,
                2 * ux * uy, 2 * uy * uy - 1, 0, 0, 0, 1 });
        // Since the side point is not the origin, we should do the translate it
        // to origin first, then back to side point.
        matrix.preTranslate(-mSidePoint.x, -mSidePoint.y);
        matrix.postTranslate(mSidePoint.x, mSidePoint.y);
        // Save the state of the canvas so that it can be reuse.
        canvas.save();
        // Restrict drawing area.
        canvas.clipPath(upper);
        // Draw the paper back.
        canvas.drawBitmap(child, matrix, paint);
        // Restore the canvas.
        canvas.restore();
        // Restore the alpha

        return upper;
    }


    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        if (child == mFrontView) {
            // always draw front view
            // Log.d(null, "drawChild " + child);
            super.drawChild(canvas, child, drawingTime);
        } else if (mCanScroll && isScrolling() && child == mBackgroundView) {
            // only draw background view when scrolling
            // Log.d(null, "drawChild " + child);
            super.drawChild(canvas, child, drawingTime);
        } else if (!mCanScroll) {
            // draw edge view
            if (child == mLeftEdge) {
                super.drawChild(canvas, child, drawingTime);
            } else if (child == mRightEdge) {
                super.drawChild(canvas, child, drawingTime);
            } else if (child == mTopEdge) {
                super.drawChild(canvas, child, drawingTime);
            } else if (child == mBottomEdge) {
                super.drawChild(canvas, child, drawingTime);
            }
        }
        return false;
    }

    private void drawCurrentPageArea(final Canvas canvas, final Bitmap child, final Path downer
            ) {
        // We merge the area that is already mTurnPagePaint
        final Path clipPath = new Path(downer);
        // clipPath.addPath(downer);
        final int saveToRestore = canvas.getSaveCount();
        canvas.save();
        // Exclude the part we have already mTurnPagePaint.
        canvas.clipPath(clipPath, Op.DIFFERENCE);
//        super.drawChild(canvas, child, 0);
        canvas.drawBitmap(child, 0,0, null);
        // Draw current page.
        canvas.restoreToCount(saveToRestore);
    }

    private Path calculateDowner(final PointF sideStart,
            final PointF sideStartTangentIntersect, final PointF sideTangent,
            final PointF bottomTangent,
            final PointF bottomStartTangentIntersect, final PointF bottomStart,
            final PointF peakPoint) {
        final Path downer = new Path();

        // draw the down triangle
        downer.reset();
        downer.moveTo(sideStart.x, sideStart.y);
        downer.quadTo(sideStartTangentIntersect.x, sideStartTangentIntersect.y,
                sideTangent.x, sideTangent.y);
        downer.lineTo(bottomTangent.x, bottomTangent.y);
        downer.quadTo(bottomStartTangentIntersect.x,
                bottomStartTangentIntersect.y, bottomStart.x, bottomStart.y);
        downer.lineTo(peakPoint.x, peakPoint.y);
        downer.lineTo(sideStart.x, sideStart.y);
        final RectF translateRectF = new RectF();
        downer.computeBounds(translateRectF, false);

        downer.close();
        return downer;

    }

    private Path drawNextPageArea(final Canvas canvas, final PointF sideStart,
            final PointF sideStartTangentIntersect, final PointF sideTangent,
            final PointF bottomTangent,
            final PointF bottomStartTangentIntersect, final PointF bottomStart,
            final PointF peakPoint, final Bitmap child, final long drawingTime) {
        final Path downer = calculateDowner(sideStart,
                sideStartTangentIntersect, sideTangent, bottomTangent,
                bottomStartTangentIntersect, bottomStart, peakPoint);
        final int saveToRestore = canvas.getSaveCount();
        canvas.save();
        canvas.clipPath(downer);
//        super.drawChild(canvas, child, drawingTime);
        canvas.drawBitmap(child, 0,0, null);
        canvas.restoreToCount(saveToRestore);
        return downer;
    }

    protected boolean oriDrawChild(Canvas canvas, View child, long drawingTime) {
        return super.drawChild(canvas, child, drawingTime);
    }

    protected void allScrollFinished() {
        Log.d(null, "allScrollFinished");
        mIsScrollerScrolling = false;
        mCanScroll = false;
        if (mScrollToView != mFrontView) {
            swapFontAndBackground();
            if (mPageScrollListener != null) {
                mPageScrollListener.onScrollFinished(mScrollOrientation,
                        mScrollToPageIndex);
            }
        } else {
            mBackgroundView.setVisibility(INVISIBLE);
            mMoveX = 0;
            mMoveY = 0;
            if (mPageScrollListener != null) {
                mPageScrollListener.onScrollReseted(mResetScrollOrientation);
            }
        }
        Log.d(null, "mScrollToView=" + mScrollToView);
        Log.d(null, "mFrontView=" + mFrontView);
        Log.d(null, "mBackgroundView=" + mBackgroundView);
        mScrollToPageIndex = SCROLL_TO_INDEX_UNSPECIFIED;
        mScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
        mResetScrollOrientation = SCROLL_ORIENTATION_UNKNOW;
    }

    private void scrollToPageInner(int scrollOrientation) {
        final int width = getWidth();
        final int height = getHeight();
        int dx = 0;
        int dy = 0;
        final int moveX = (int) mMoveX;
        final int moveY = (int) mMoveY;
        switch (scrollOrientation) {
        case SCROLL_ORIENTATION_BOTTOM_TO_TOP:
            dx = 0;
            dy = -height - moveY;
            mScrollToView = mBackgroundView;
            break;
        case SCROLL_ORIENTATION_TOP_TO_BOTTOM:
            dx = 0;
            dy = height - moveY;
            mScrollToView = mBackgroundView;
            break;
        case SCROLL_ORIENTATION_LEFT_TO_RIGHT:
            dx = width - moveX;
            dy = 0;
            mScrollToView = mBackgroundView;
            break;
        case SCROLL_ORIENTATION_RIGHT_TO_LEFT:
            dx = -width - moveX;
            dy = 0;
            mScrollToView = mBackgroundView;
            break;
        default:
            dx = -moveX;
            dy = -moveY;
            mScrollToView = mFrontView;
            break;
        }
        mIsScrollerScrolling = true;
        mScroller.startScroll(moveX, moveY, dx, dy);
        invalidate();
    }

    public void scrollToPage(int scrollOrientation, int scrollToPageIndex) {
        mScrollOrientation = scrollOrientation;
        mScrollToPageIndex = scrollToPageIndex;
        if (mPageScrollListener != null) {
            mBackgroundView.setVisibility(VISIBLE);
            mCanScroll = mPageScrollListener.onScrollStarted(scrollOrientation,
                    mBackgroundView, scrollToPageIndex);
            mIsPagePrepared = false;
        }
        if(mPageTurningEnabled) {
            mTurnPageMode = scrollOrientation == SCROLL_ORIENTATION_LEFT_TO_RIGHT ? MODE_TURN_PRE_PAGE : MODE_TURN_NEXT_PAGE;
            startAutoScrolling();
        } else {
            scrollToPageInner(scrollOrientation);
        }
    }

    /**
     * Retrieve the frontView.
     *
     * @return the frontView
     */
    public View getFrontView() {
        return mFrontView;
    }

    /**
     * Set the value of frontView.
     *
     * @param frontView
     *            the frontView to set
     */
    public void setFrontView(View frontView) {
        mFrontView = frontView;
    }

    /**
     * Retrieve the backgroundView.
     *
     * @return the backgroundView
     */
    public View getBackgroundView() {
        return mBackgroundView;
    }

    /**
     * Set the value of backgroundView.
     *
     * @param backgroundView
     *            the backgroundView to set
     */
    public void setBackgroundView(View backgroundView) {
        mBackgroundView = backgroundView;
    }

    public void swapFontAndBackground() {
        Log.d(null, "swapFontAndBackground");
        final View temp = mBackgroundView;
        mBackgroundView = mFrontView;
        mFrontView = temp;
        mFrontView.setVisibility(VISIBLE);
        mBackgroundView.setVisibility(INVISIBLE);
        mMoveX = 0;
        mMoveY = 0;
    }

    /**
     * Set the value of leftEdge.
     *
     * @param leftEdge
     *            the leftEdge to set
     */
    public void setLeftEdge(View leftEdge) {
        mLeftEdge = leftEdge;
    }

    /**
     * Set the value of topEdge.
     *
     * @param topEdge
     *            the topEdge to set
     */
    public void setTopEdge(View topEdge) {
        mTopEdge = topEdge;
    }

    /**
     * Set the value of bottomEdge.
     *
     * @param bottomEdge
     *            the bottomEdge to set
     */
    public void setBottomEdge(View bottomEdge) {
        mBottomEdge = bottomEdge;
    }

    /**
     * Set the value of rightEdge.
     *
     * @param rightEdge
     *            the rightEdge to set
     */
    public void setRightEdge(View rightEdge) {
        mRightEdge = rightEdge;
    }

    public class Vector {
        public Vector() {

        }

        public Vector(final float x, final float y) {
            this.x = x;
            this.y = y;
        }

        public float x;
        public float y;
    }

}
